package ircbot;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.ArrayList;

/**
 * @author TheNamelssOne
 * @version 0.1 date 16/01 2012 + Connect to server. + Join channels. + Answers
 * to Ping.
 *
 * @version 0.2 date 17/01 2012 + Commands for Op, Deop, Restart and Stop +
 * Above commands are secured by password.
 *
 * @version 0.3 date 17/01 2012 + Can now be invited to channels during runtime.
 * + New command: Leave, Leaves the channel. + New public command: Info, prints
 * info about Cerberus.
 *
 * @version 0.3.1 date 20/01 2012 + Public command "temp" added. Displays the
 * core temperature of the host machine. (LINUX SYSTEMS ONLY). + Public command
 * "uptime" added. Displays the host system uptime. (LINUX MACHINES ONLY).
 *
 * @version 0.4 date ? Config is now read from config.txt. Can now op/deop
 * several people at the same time.
 * 
 * @version 0.5 date ? Possible to enable password protected channel/nickname. 
 */
public class IRCBot {

    private int PORT = 6667;
    private long born = -1;
    private String URL = "irc.onlinegamesnet.net";
    private String nick = "Cerberus";
    private String ident = "Cerberus";
    private String realname = "CerberusGuardian";
    private String owner = "TheNamelssOne.com";
    private String cmdpass = "bloatthis";
    private String version = "0.3.1";
    private ArrayList<String> channels = new ArrayList<String>();
    private ArrayList<String> ops = new ArrayList<String>();
    private ArrayList<String> passedops = new ArrayList<String>();
    private ArrayList<String> cmds = new ArrayList<String>();
    private Socket con = null;
    private DataInputStream is = null;
    private PrintStream os = null;
    private BufferedReader br = null;
    
    public IRCBot() {
        this.born = System.currentTimeMillis();
        
        this.cmds.add(".op");
        this.cmds.add(".deop");
        this.cmds.add(".restart");
        this.cmds.add(".stop");
        this.cmds.add(".leave");
        //this.channels.add("#TGF.Electra");
    }

    public IRCBot(String URL, int PORT) {
        this.PORT = PORT;
        this.URL = URL;
        this.born = System.currentTimeMillis();

        this.cmds.add(".op");
        this.cmds.add(".deop");
        this.cmds.add(".restart");
        this.cmds.add(".stop");
        this.cmds.add(".leave");
        //this.channels.add("#TGF.Electra");
    }

    private void reset() {
        //this.channels.add("#cp");
        //this.channels.add("#test");

        this.ops.add("TheNamelssOne");
        this.ops.add("Zhoot");
    }

    private void close() {
        try {
            this.con.close();
        } catch (Exception e) {
            System.err.println(e);
        }
    }

    public void connect() {
        this.reset();
        int connecttries = 0, maxtries = 5, waittime = 1000;


        do {
            try {
                System.out.println("Connecting " + this.URL + ":" + this.PORT + ((connecttries > 0) ? "(" + connecttries + ")" : ""));
                this.con = new Socket(this.URL, this.PORT);
            } catch (IOException e) {
                connecttries++;
                try {
                    Thread.sleep(waittime);
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
                waittime *= 2;
            }
        } while (!this.con.isConnected() && connecttries < maxtries);



        if (this.con.isConnected()) {
            this.sayhello();
        }
    }

    public void sayhello() {
        try {
            System.out.println("Connected.\nLogging in.");
            this.is = new DataInputStream(this.con.getInputStream());
            this.os = new PrintStream(this.con.getOutputStream());
            this.br = new BufferedReader(new InputStreamReader(this.is));

            this.tellserver("NICK " + this.nick + "\n");
            this.tellserver("USER " + this.ident + " " + this.URL + " bla :" + this.realname + "\n");
            this.artiint();
        } catch (IOException e) {
            System.err.println(e);
        }
    }

    public void artiint() {
        boolean run = true;
        String line = "", tmp[];

        System.out.println("Logged in..");
        try {
            for (int i = 0; i < this.channels.size(); i++) {
                this.tellserver("JOIN " + this.channels.get(i) + "\n");
            }
            while (run) {
                if ((line = this.br.readLine()) != null) {
                    tmp = line.split(":");
                    System.out.println(line);
                    if (tmp[0].contains("PING")) {
                        this.tellserver("PONG :" + tmp[1] + "\n");
                    } else if (line.contains("PRIVMSG") || line.contains("INVITE")) {
                        this.handlemsg(line);
                    }
                }
            }
        } catch (Exception e) {
            System.err.println(e);
        }
    }

    private void tellserver(String msg) {
        try {
            this.os.println(msg);
            System.out.println("> " + msg);
        } catch (Exception e) {
            System.err.println(e);
        }
    }

    private void tellallchannels(String msg) {
        for (int i = 0; i < this.channels.size(); i++) {
            this.tellserver("PRIVMSG " + this.channels.get(i) + " :" + msg + "\n");
        }
    }

    private void joinchannel(String chn) {
        this.tellserver("JOIN " + chn + "\n");
        if (!this.channels.contains(chn)) {
            this.channels.add(chn);
        }
    }

    // http://osix.net/modules/article/?id=780
    private void handlemsg(String msg) {
        String msgpart = "",
                info = "",
                sender = "",
                temp = "",
                host = "",
                channel = "",
                target = "",
                cmd = "",
                action = "";
        String mess[] = new String[5];

        String tmp[];


        // Split up the incoming msg into its seperate parts.
        tmp = msg.split(":");
        if (tmp.length > 2) {
            msgpart = tmp[2];
            info = tmp[1];
            tmp = info.split("!");
            sender = tmp[0];
            tmp = tmp[1].split(" ");
            host = tmp[0];
            action = tmp[1];
            tmp = info.split(" ");
            channel = tmp[2];
            tmp = msgpart.split(" ");
            cmd = tmp[0];
            if (tmp.length > 1) {
                target = tmp[1];
            }

            mess[0] = sender;
            mess[1] = host;
            mess[2] = action;
            mess[3] = channel;
            mess[4] = msgpart;

            // Action processing.
            if (action.equalsIgnoreCase("invite")) {
                this.joinchannel(msgpart);
            }

            // Password login.
            if (msgpart.startsWith("." + this.cmdpass) && this.ops.contains(sender)) {
                if (!this.passedops.contains(host)) {
                    this.passedops.add(host);
                }
                this.tellserver("PRIVMSG " + sender + " :Password Accepted.\n");
            } else if (msgpart.startsWith("." + this.cmdpass) && !this.ops.contains(sender)) {
                this.tellserver("PRIVMSG " + sender + " :You're not cleared for OP access.\n");
            }

            // OP Commands.
            if (this.cmds.contains(cmd)) {
                if (this.passedops.contains(host)) {

                    // OP command.
                    if (cmd.startsWith(this.cmds.get(0))) {
                        this.op(mess);
                    }

                    /*
                     * if (cmd.startsWith(this.cmds.get(0)) &&
                     * !target.equals("")) { this.tellserver("MODE " + channel +
                     * " +o " + target + "\n"); } else if
                     * (cmd.startsWith(this.cmds.get(0)) && target.equals("")) {
                     * this.tellserver("MODE " + channel + " +o " + sender +
                     * "\n"); }
                     */

                    // De-op command.
                    if (cmd.startsWith(this.cmds.get(1))) {
                        this.deop(mess);
                    }

                    /*
                     * if (cmd.startsWith(this.cmds.get(1)) &&
                     * !target.equals("")) { this.tellserver("MODE " + channel +
                     * " -o " + target + "\n"); } else if
                     * (cmd.startsWith(this.cmds.get(1)) && target.equals("")) {
                     * this.tellserver("MODE " + channel + " -o " + sender +
                     * "\n"); }
                     */

                    // Restart command.
                    if (cmd.startsWith(this.cmds.get(2))) {
                        this.tellallchannels("I'll be back.");
                        this.close();
                        this.connect();
                        this.artiint();
                    }

                    // Stop command.
                    if (cmd.startsWith(this.cmds.get(3))) {
                        this.tellallchannels("Good bye.");
                        this.close();
                        System.exit(0);
                    }

                    // Leave command.
                    if (cmd.startsWith(this.cmds.get(4))) {
                        this.tellserver("PART " + channel + "\n");
                    }
                } else {
                    this.tellserver("PRIVMSG " + sender + " :You need to log in to use this command.");
                }
            }

            // Public Commands.

            if (cmd.startsWith(".info")) {
                this.printInfo(mess);
            }

            if (cmd.startsWith(".uptime")) {
                this.uptime(mess);
            }
            
            if(cmd.startsWith(".temp")) {
                this.temp(mess);
            }
        }
    }

    private void op(String msg[]) {
        String tmp[] = msg[4].split(" ");

        if (tmp.length == 1) {
            this.tellserver("MODE " + msg[3] + " +o " + msg[0] + "\n");
        } else if (tmp.length > 1) {
            for (int a = 1; a < tmp.length; a++) {
                this.tellserver("MODE " + msg[3] + " +o " + tmp[a] + "\n");
            }
        }
    }

    private void deop(String msg[]) {
        String tmp[] = msg[4].split(" ");

        if (tmp.length == 1) {
            this.tellserver("MODE " + msg[3] + " -o " + msg[0] + "\n");
        } else if (tmp.length > 1) {
            for (int a = 1; a < tmp.length; a++) {
                this.tellserver("MODE " + msg[3] + " -o " + tmp[a] + "\n");
            }
        }
    }

    private void printInfo(String msg[]) {
        int alive = (int) (System.currentTimeMillis() - this.born) / 1000,
                days,
                hours,
                minutes,
                seconds;
        String aliveprint = "";
        String commands = "";

        for (int i = 0; i < this.cmds.size(); i++) {
            commands += " " + this.cmds.get(i);
        }

        days = (int) alive / 86400;
        alive = alive % 86400;
        hours = (int) alive / 3600;
        alive = alive % 3600;
        minutes = (int) alive / 60;
        seconds = alive % 60;
        aliveprint += (days > 0) ? days + " d " : "";
        aliveprint += (hours > 0) ? hours + "h " : "";
        aliveprint += (minutes > 0) ? minutes + "m " : "";
        aliveprint += (seconds > 0) ? seconds + "s." : ".";

        this.tellserver("PRIVMSG " + msg[3] + " : I'm " + this.nick + "(v" + this.version + "). My creator and owner is " + this.owner + ".");
        this.tellserver("PRIVMSG " + msg[3] + " : I've been online for " + aliveprint + " My commands are: .info" + commands);

    }

    private void uptime(String msg[]) {
        String shellcmd = "uptime";
        String line = "";

        try {
            Process p = Runtime.getRuntime().exec(shellcmd);
            BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
            while ((line = in.readLine()) != null) {
                this.tellserver("PRIVMSG " + msg[3] + " :" + line);
            }

        } catch (Exception e) {
            System.out.println(e);
        }
    }
    
    private void temp(String msg[]){
        String shellcmd = "sensors";
        String line = "";
        boolean written = false;
        
        try {
            Process p = Runtime.getRuntime().exec(shellcmd);
            BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
            while((line = in.readLine()) != null){
                if(line.contains("temp1")){
                    this.tellserver("PRIVMSG " + msg[3] + " :" + line);
                    written = true;
                }
            }
        } catch(Exception e){
            System.out.println(e);
        }
        if(!written) this.tellserver("PRIVMSG " + msg[3] + " :This command is not supported on this host");
    }
}